package kaalanshare;

import java.io.*;
import java.net.*;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

public class FolderStructure implements Serializable {
    private HashMap<String, FolderStructure> Folders;
    private LinkedList<String> Files;
    private static LinkedList<InetAddress> addresses=new LinkedList<>();
    String name;

    FolderStructure(File folder) {
        addresses=getBroadcastAddresses();
        setFolder(folder);
    }

    // Build folder structure
    public void setFolder(File folder) {
        if(!folder.exists()){
            folder.mkdir();
        }
        File[] listOfFiles = folder.listFiles();
        name = folder.getName();
        Folders = new HashMap<String, FolderStructure>();
        Files = new LinkedList<String>();

        for (int i = 0; i < listOfFiles.length; i++) {
            if (listOfFiles[i].isFile()) {
                System.out.println("File " + listOfFiles[i].getName());
                Files.push(listOfFiles[i].getName());

            } else if (listOfFiles[i].isDirectory()) {
                System.out.println("Directory " + listOfFiles[i].getName());
                Folders.put(listOfFiles[i].getName(), new FolderStructure(listOfFiles[i]));
            }
        }

    }

    // Convert kaalanshare.FolderStructure to ByteArray
    public byte[] toByteArray() {
        ByteArrayOutputStream bStream = new ByteArrayOutputStream();
        ObjectOutputStream oStream = null;
        try {
            oStream = new ObjectOutputStream(bStream);
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        try {
            oStream.writeObject(this);
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        return bStream.toByteArray();
    }

    //Broadcast folder structure on all interfaces with broadcast address
    public void broadcastFolder() {
        byte[] byteVal = toByteArray();
        System.out.println(byteVal.length);

        int port = 1991;
        DatagramSocket serverSocket = null;

        try {
            serverSocket = new DatagramSocket();
        } catch (SocketException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        try {
            serverSocket.setBroadcast(true);
        } catch (SocketException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        for (InetAddress IPAddress : addresses) {
            DatagramPacket sendPacket1 = new DatagramPacket(byteVal, byteVal.length, IPAddress, port);
            try {
                serverSocket.send(sendPacket1);
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
            System.out.println("Sending Discovery message to " + IPAddress.getHostAddress() + " Via UDP port " + port);
        }
    }

    // Get FolderStruct by requested file url
    public LinkedList<String> getFolderStructByUrl(String fileUrl) {
        String[] fileNames = fileUrl.split(System.getProperty("file.separator"));
        FolderStructure fold = this;
        LinkedList<String> files = new LinkedList<String>();
        for (int i = 0; i < fileNames.length - 1; ++i) {
            if ((fold = fold.Folders.get(fileNames[i])) == null) {
                break;
            }
        }
        if (fold != null) {
            if (fold.Files.contains(fileNames[fileNames.length - 1])) {
                files.push(fileUrl);

            } else if (fileNames[fileNames.length - 1].equals("*")) {
                files.addAll(getFilesFromFolder(fold, fileUrl.substring(0, fileUrl.length() - 1)));
            } else if ((fold = fold.Folders.get(fileNames[fileNames.length - 1])) != null) {
                for (String s : fold.Files) {
                    files.push(fileUrl + System.getProperty("file.separator") + s);
                }
                for (FolderStructure structure : fold.Folders.values()) {
                    files.addAll(getFilesFromFolder(structure, fileUrl + System.getProperty("file.separator") + structure.name));
                }
            } else {
                return files;
            }
        }
        return files;
    }


    private LinkedList<String> getFilesFromFolder(FolderStructure folderStructure, String url) {
        LinkedList<String> file = new LinkedList<String>();
        url += System.getProperty("file.separator");
        url = url.replaceAll("^/", "").replaceAll(System.getProperty("file.separator") + System.getProperty("file.separator"), System.getProperty("file.separator"));

        for (String s : folderStructure.Files) {
            file.push(url + s);
        }
        for (FolderStructure fold : folderStructure.Folders.values()) {
            file.addAll(getFilesFromFolder(fold, url + fold.name));
        }

        return file;
    }

    public String printFolderStructure() {
        return printFolderStructure(this, "");

    }

    private String printFolderStructure(FolderStructure fold, String url) {
        String str = "";
        for (String s : fold.Files) {
            str += "\t" + url + s + "\n";
        }
        for (FolderStructure folder : fold.Folders.values()) {
            str += printFolderStructure(folder, url + folder.name + "/");
        }
        return str;
    }

    // Share file creating symbolic lync to it
    public static void lnShareFile(String fullpath, String relativeName) {
        Process process = null;
        try {
            String[] command=new String[]{"ln", "-s", fullpath, relativeName};
            process = Runtime.getRuntime().exec(command);
            System.out.println("file added");
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        try {
            process.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        process.destroy();
    }

    // Share file copying it
    public static void cpShareFile(File sourceLocation, File targetLocation) throws IOException {

        if (sourceLocation.isDirectory()) {
            if (!targetLocation.exists()) {
                targetLocation.mkdir();
            }

            String[] children = sourceLocation.list();
            for (int i=0; i<children.length; i++) {
                cpShareFile(new File(sourceLocation, children[i]),
                        new File(targetLocation, children[i]));
            }
        } else {

            InputStream in = new FileInputStream(sourceLocation);
            OutputStream out = new FileOutputStream(targetLocation);

            // Copy the bits from instream to outstream
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
            System.out.println("file added");
        }
    }

    //Get List of broadcast addresses from all interfaces
    public static LinkedList<InetAddress> getBroadcastAddresses() {
        Enumeration interfaces = null;
        LinkedList<InetAddress> addressesList = new LinkedList<InetAddress>();
        try {
            interfaces = NetworkInterface.getNetworkInterfaces();

        } catch (SocketException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        while (interfaces.hasMoreElements()) {
            NetworkInterface ni = (NetworkInterface) interfaces.nextElement();
            List<InterfaceAddress> addresses = ni.getInterfaceAddresses();
            for (InterfaceAddress addr : addresses) {
                InetAddress address = addr.getBroadcast();
                if (address != null) {
                    addressesList.push(address);
                    System.out.println(address.getHostAddress());
                }
            }
        }
        return addressesList;
    }
}
